<?php

// Application

class App{

    static $part = 'root';
    static $module = 'home';
    //public $ctrl = null;

    function __construct($run=1){ 
        if($run){ $this->run(); }
    }

    # === Base =======================================

    // 运行控制器方法
    function run(){
        try{
            $this->runPre(); 
            $gmkv = $this->route(); 
            $mkvs = $this->mkvs($gmkv['part'], $gmkv['mkv']);
            $ctrl = self::getCtrl($mkvs);
            $ctrl->doAction(); 
            $this->runEnd();
        }catch(Exception $ex){
            $ctrl = new Ctrl();
            $ctrl->error($ex, 'App-Run');
        }
    }
    // 前置处理, 如果需要, 重写此方法
    function runPre(){
        global $_cbase;
    }
    // 后置处理, 如果需要, 重写此方法
    function runEnd(){ 
        #Debug::vrSave(); // 日志收集(尽量用web服务器收集，不行的话再考虑用此方法)
    }

    // 获取控制器
    static function getCtrl($mkvs){
        $part = $mkvs['part'];
        $class = $mkvs['mod'].'Ctrl'; # ucfirst()
        $path = DIR_CTRL."/$part/$class.php";
        if(file_exists($path)){
            require $path;
            if(substr($class,0,1)<'A'){ $class = "_$class"; } // 数字开头加`_`, eg:_3dCtrl
            return new $class($mkvs);
        }else{
            return new Ctrl($mkvs);
        }
    }

    # === Route =======================================

    // route-路由分析
    static function route(){ // $gmkv='' - 给定参数?
        // init-str
        if(PHP_SAPI=='cli'){ // 命令行: index.php root/home stype:;keywd:php5.4
            $gmkv = empty($_SERVER['argv'][1]) ? '' : $_SERVER['argv'][1];
            $q = empty($_SERVER['argv'][2]) ? '' : str_replace([':',';'], ['=','&'], $_SERVER['argv'][2]);
            $_SERVER["REQUEST_URI"] = $_SERVER['argv'][0].'?'.$q;
            $_SERVER['QUERY_STRING'] = $q; 
            $q ? parse_str($q, $_GET) : $_GET=[]; 
        }else{ // url: /index.php[?r=root/home-index]/[/root/home]
            $r = empty($_GET['r']) ? '' : $_GET['r']; 
            $p = empty($_SERVER['PATH_INFO']) ? '' : substr($_SERVER['PATH_INFO'],1);
            $gmkv = $p ? $p : $r;
            $q = $_SERVER['QUERY_STRING'];
        }
        // 
        preg_match("/^([\w]+\/)?([\w\.\-]+)?$/", $gmkv, $itms);
        if($gmkv && (empty($itms) || preg_match("/(\.|\-)+$/",$gmkv))){
            abort("Error-RouteValue: `$gmkv` ", 600);
        }
        if(preg_match("/\<|\>|\"|\'|\\|\r|\n/", urldecode($q))){
            abort("Error-QUERY_STRING: `$q` ", 600);
        }
        if(defined('PART') && !empty($itms[1])){ // 绑定分组
            abort("Error-RouteGroup: `".str_replace('/','',$itms[1])."` ", 600);
        }
        $part = empty($itms[1]) ? (defined('PART') ? PART : self::$part) : str_replace('/','',$itms[1]);
        $mkv = empty($itms[2]) ? self::$module : $itms[2];
        return ['part'=>$part, 'mkv'=>$mkv];
    }

    # mkv-参数分析
    static function mkvs($part='', $mkv=''){
        global $_cbase; 
        if($mkv=='index.php'){ $mkv='home'; }
        # spilt-mkv
        preg_match("/^\w+([\.|\-]{1})/", $mkv, $it); 
        if(empty($it[1])){ 
            $mode = 'home';
            $mod = $mkv; $key = ''; $view = '';
        }else{
            $mode = $it[1]=='.' ? 'view' : 'list';
            $tmp = $mode=='view' ? explode('.',$mkv) : explode('-',$mkv);
            $mod = $tmp[0]; $key = $tmp[1]; $view = empty($tmp[2]) ? '' : $tmp[2];
            if(!empty($tmp[3])){ for($i=3;$i<count($tmp);$i++){ $view .= ($mode=='view' ? '.' : '-') . $tmp[$i]; } }
        } //echo "$mode <br> $mod <br> $key <br> $view";
        # tpname
        $tpdir = "$part/$mod/"; $tpone = '';
        if($mode=='home'){
            $tpfile = '_home';
        }elseif($mode=='list'){
            $tpfile = '_list' . ($view ? "-$view" : ''); 
            $tpone = $key . ($view ? "-$view" : '');
        }else{ // view
            $tpfile = '_view' . ($view ? ".$view" : ''); 
            $tpone = $key . ($view ? ".$view" : '');
        }
        # check
        if(in_array($key,['_home','_list','_view'])){
            abort("Error-RouteKey:`$key`", 600);
        }
        # return-mkvs
        $_cbase['mkvs'] = $mkvs = [
            'part'=>$part, 'mkv'=>$mkv, 'type'=>$mode, 
            'mod'=>$mod, 'key'=>$key, 'view'=>$view, 
            'tpdef'=>$tpdir.$tpfile, 'tpone'=>$tpone?$tpdir.$tpone:'',
        ]; 
        return $mkvs;
    }

    # === AutoLoader =======================================

    // 自动加载 - 这个效率比 composer 高些
    static function autoLoad($class){ //echo "auto@$class,<br>\n ";
        global $_cbase; 
        $ftr = DIR_CTRL."/trait/$class.php";
        $fbs = DIR_CTRL."/{$_cbase['mkvs']['part']}/$class.php";
        if(substr($class,-5)=='Trait' && file_exists($ftr)){
            require $ftr;
        }elseif(substr($class,0,5)=='_base' && file_exists($fbs)){
            require $fbs;
        }else{
            global $_cbase; $dirs = $_cbase['loads']; // 加载目录
            foreach($dirs as $dir){
                $dir = substr($dir,0,4)=='app.' ? str_replace('app.', DIR_APP.'/', $dir) : DIR_YSKIT."/$dir";
                $fp1 = "$dir/$class.php";
                if(file_exists($fp1)){ return require $fp1; }
            }
            return false;
        }
    }

}
